//
// Created by jiaxv on 25-8-15.
//

#include <iso646.h>

#include "as_entity.h"
#include "define.h"
#include "interface/ground.h"


static l_err recv_g7_bearer_command(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);
static l_err recv_g7_bearer_complete(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt);


size_t g7_format_size = 2;
cn_format_desc_t g7_format_descs[] = {
    {G7_DATA_BEARER_SESSION_COMMAND, &g7_bearer_command_desc, "G7 BEARER COMMAND", sizeof(g7_bearer_command_t), 0, default_cn_send_handler, recv_g7_bearer_command, NULL, free_g7_bearer_command},
    {G7_DATA_BEARER_SESSION_COMPLETE, &g7_bearer_complete_desc, "G7 BEARER COMPLETE", sizeof(g7_bearer_complete_t), 4, default_cn_send_handler, recv_g7_bearer_complete, NULL, free},
};

static l_err recv_g7_bearer_command(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {

    g7_bearer_command_t *cmd = calloc(1, sizeof(g7_bearer_command_t));
    PARSE_DSTR_PKT(in_buf, cmd, sdu, g7_bearer_command_desc, G7_BEARER_HEADER_LEN, 0)


    switch (cmd->CMD_TYPE) {
        case ACCESS: {
            g7_bearer_command_type1_t *type1 = NULL;
            if ((type1 = parse_sdu(cmd->sdu, &g7_bearer_command_type1_desc, sizeof(g7_bearer_command_type1_t))) == NULL) {
                log_error("Parse SDU failed!");
                desc->free_func(cmd);
                return LD_ERR_INTERNAL;
            }

            if (has_as_senode(net_ele->as_map, cmd->AS_SAC)) {
                // 切换的时候用
            }else {
                as_entity_t *as = init_as_entity_ip(cmd->AS_SAC, (char *)type1->IP_AS->ptr, (char *)type1->IP_GS->ptr, type1->port_gs);
                // 设定AS当前接口的回复propt
                set_as_enode(net_ele->as_map, as);
            }

            free_g7_bearer_type1_command(type1);
            break;

        }
        case SWITCH: {
            g7_bearer_command_type2_t *type2 = NULL;
            if ((type2 = parse_sdu(cmd->sdu, &g7_bearer_command_type2_desc, sizeof(g7_bearer_command_type2_t))) == NULL) {
                log_error("Parse SDU failed!");
                desc->free_func(cmd);
                return LD_ERR_INTERNAL;
            }

            as_entity_t *as = get_as_senode_by_SAC(net_ele->as_map, cmd->AS_SAC);
            if (!as) {
                desc->free_func(cmd);
                free_g7_bearer_type1_command(type2);
                cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "No such AS, which SAC = %d", cmd->AS_SAC);
                return LD_ERR_INTERNAL;
            }

            // log_buf(LOG_ERROR, "IPPPP", type2->IP_GS->ptr, GEN_ADDRLEN);

            if (set_as_gs_ip(as, (char *)type2->IP_GS->ptr, type2->port_gs) != LD_OK) {
                desc->free_func(cmd);
                free_g7_bearer_type1_command(type2);
                cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "Cannot set IP");
                return LD_ERR_INTERNAL;
            }

            free_g7_bearer_type2_command(type2);
            break;
        }
        case EXIT: {
            as_entity_t *as = get_as_senode_by_SAC(net_ele->as_map, cmd->AS_SAC);
            if (!as) {
                desc->free_func(cmd);
                cn_log_error(net_ele->element_tag, INTERFACE_G7, "No such AS, which SAC = %d", cmd->AS_SAC);
                return LD_ERR_INVALID;
            }

            if (delete_element_senode_by_SAC(net_ele->as_map, cmd->AS_SAC) != LD_OK) {
                cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "Failed to delete as entity");
                desc->free_func(cmd);
                return LD_ERR_INVALID;
            }
            break;
        }
        default: {
            desc->free_func(cmd);
            cn_log_error(net_ele->element_tag, INTERFACE_G7, "Invalid command type: %d", cmd->CMD_TYPE);
            return LD_ERR_INVALID;
        }
    }

    g7_bearer_complete_t complete = {
        .GTYPE = G7_DATA_BEARER_SESSION_COMPLETE,
        .VERSION = CN_VERSION,
        .AS_SAC = cmd->AS_SAC,
        .CMD_TYPE = cmd->CMD_TYPE,
    };

    cn_format_desc_t *send_desc = get_non_sequential_desc(g7_format_size, g7_format_descs, G7_DATA_BEARER_SESSION_COMPLETE);
    if (!send_desc || !send_desc->send_handler || send_desc->send_handler(
            net_ele,
            &complete,
            send_desc,
            get_propt(net_ele, INTERFACE_G7, 0),
            INTERFACE_G7
        ) != LD_OK) {
        cn_log_error(net_ele->element_tag, INTERFACE_G7, "Failed to send");
        desc->free_func(cmd);
        return LD_ERR_NULL;
        }

    desc->free_func(cmd);

    return LD_OK;

}

static l_err recv_g7_bearer_complete(net_element_t *net_ele, cn_format_desc_t *desc, buffer_t *in_buf, element_propt_t *propt) {
    g7_bearer_complete_t *complete = NULL;
    if ((complete = parse_sdu(in_buf, desc->f_desc, desc->struct_size)) == NULL) {
        log_error("Parse SDU failed!");
        if (complete)    desc->free_func(complete);
        return LD_ERR_INTERNAL;
    }

    as_entity_t *as = get_as_senode_by_SAC(net_ele->as_map, complete->AS_SAC);
    if (!as) {
        desc->free_func(complete);
        cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "No such AS, which SAC = %d", complete->AS_SAC);
        return LD_ERR_INTERNAL;
    }

    if (complete->CMD_TYPE == ACCESS) {
        if (change_state(&as->dbsm_sm, EV_DEFAULT,
                         &(fsm_event_data_t){
                             &dbsm_fsm_events[DBSM_CONNECTED], NULL
                         })) {
            cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "cant change state correctly");
            desc->free_func(complete);
            return LD_ERR_INVALID;
        }
    }


    g1_g3_g5_as_st_chg_t chg = {
        .GTYPE = G5_AS_STATE_CHANGE,
        .VERSION = CN_VERSION,
        .AS_SAC = complete->AS_SAC,
        .STATE = complete->CMD_TYPE,
        .GS_SAC = 0x10,
    };

    cn_format_desc_t *send_desc = get_non_sequential_desc(g5_format_size, g5_format_descs, G5_AS_STATE_CHANGE);
    if (!send_desc || !send_desc->send_handler || send_desc->send_handler(
            net_ele,
            &chg,
            send_desc,
            get_propt(net_ele, INTERFACE_G5, 0),
            INTERFACE_G5
        ) != LD_OK) {
        cn_log_error(net_ele->element_tag, INTERFACE_G5, "Failed to send");
        desc->free_func(complete);
        return LD_ERR_NULL;
    }

    if (complete->CMD_TYPE == EXIT) {
        if (change_state(&as->dbsm_sm, EV_DEFAULT,
                         &(fsm_event_data_t){
                             &dbsm_fsm_events[DBSM_EXIT], NULL
                         })) {
            cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "cant change state correctly");
            desc->free_func(exit);
            return LD_ERR_INVALID;
        }


        if (delete_element_senode_by_SAC(net_ele->as_map, as->SAC) != LD_OK) {
            cn_log_error(net_ele->element_tag, INTERFACE_UNDEFINED, "Failed to delete as entity");
            desc->free_func(complete);
            return LD_ERR_INVALID;
        }
    }

    desc->free_func(complete);
    return LD_OK;
}
